#include <unistd.h>

#include <stdio.h>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#include "load.h"
#include "string_util.h"
#include "opencv_util.h"
#include "parseCmdLine.h"
#include "NoiseAlgorithm/NoiseAlgorithm.h"

/*
 * 指定了在创建指定算法实例之后,对该实例的行为配置.
 * help;若为真,则表明此时只输出算法的帮助信息.
 * show;若为真,则表明在显示算法处理之后的图像.
 * outfile;若不为 0,则将算法处理之后的图像存放在 outfile 中.
 * infile;指定了待处理图像的路径,若为 0,则表明没有.
 */
struct AlgoBehaviorConf {
    bool help;
    bool show;
    const char *outfile;
    const char *infile;
public:
    inline 
    AlgoBehaviorConf();
};

inline
AlgoBehaviorConf::AlgoBehaviorConf()
{
    help = false;
    show = true;
    outfile = 0;
    infile = 0;
}

void
executeAlgorithm(const char *algoname,const AlgoBehaviorConf *conf,
    int argc,char **argv)
{
    cv::Ptr<NoiseAlgorithm> algo = NoiseAlgorithm::create(algoname);
    if (algo == 0) {
        fprintf(stderr,
            "ERROR;NoiseAlgorithm::create(%s): 算法不存在,或者未被加载\n",
            algoname);
        return ;
    }
    printf("INFO;成功创建 %s 实例\n",algoname);
  
    if (conf->help) {
        std::string algohelpbuf;
        algo->help(algohelpbuf);
        puts(algohelpbuf.c_str());
        return ;
    }  
    
    std::vector<std::string> arglist;
    algo->getSupportedArgs(arglist);
    std::vector<std::string>::const_iterator iter;
    std::vector<std::string>::const_iterator const end = arglist.end();
    std::vector<struct option> options;
    for (iter = arglist.begin(); iter != end; ++iter) {
        struct option opt;
        opt.name = iter->data();
        opt.has_arg = required_argument;
        opt.flag = 0;
        opt.val = '3';
        options.push_back(opt);
    }
    struct option otheropts[] = {
        {0,0,0,0}
    };
    const size_t otheroptsnum = sizeof(otheropts) / sizeof(otheropts[0]);
    for (size_t i = 0; i < otheroptsnum; ++i) 
        options.push_back(otheropts[i]);
    
    int ret;
    int longoptind = -1;
    optind = 0;
    while ((ret = parseCmdLine(argc,argv,":",options.data(),&longoptind)) != -1) {
        switch (ret) {
        case '3':
            algo->set(options[longoptind].name,optarg);
            break;
        default:
            break;
        }
    }

    printf("INFO;算法 %s 的参数设置如下:\n",algoname);
    for (iter = arglist.begin(); iter != end; ++iter) {
        std::string buf;
        printf("INFO;\t%s = %s\n",iter->c_str(),algo->get(iter->c_str(),buf));
    }

    if (conf->infile == 0) {
        fputs("ERROR;未指定待处理的图像\n",stderr);
        return ;
    }
    printf("INFO;当前处理图像: %s\n",conf->infile);

    cv::Mat inputimg = cv::imread(conf->infile,cv::IMREAD_GRAYSCALE);
    if (inputimg.data == 0) {
        fprintf(stderr,
                "ERROR;读取图像 %s;失败,"
                "可能是本地缺少对应图像格式的解码器.\n",
                conf->infile);
        return ;
    }
    printf("INFO;当前处理图像;宽度: %d;高度: %d;通道数: %d;depth: %s\n",
        inputimg.cols,inputimg.rows,
        inputimg.channels(),depth2str(inputimg.depth()));
    if (conf->show) 
        cv::imshow(conf->infile,inputimg);
    
    algo->process(inputimg);
    printf("INFO;处理之后图像;宽度: %d;高度: %d;通道数: %d;depth: %s\n",
        inputimg.cols,inputimg.rows,
        inputimg.channels(),depth2str(inputimg.depth()));
    
    if (conf->outfile != 0) {
        cv::imwrite(conf->outfile,inputimg);
        printf("INFO;图像已写入文件 %s.\n",conf->outfile);
    }

    if (conf->show) {
        std::string winname(algoname);
        winname += ".";
        winname += conf->infile;
        cv::imshow(winname,inputimg);
        cv::waitKey();
    }
    
    return ;
}

int
main(int argc,char *argv[])
{
    /* 确定 loadAlgorithm() 所需要命令行参数范围 */
    int optpart1 = 1;
    for(; optpart1 < argc; ++optpart1) {
        if (beginWith(argv[optpart1],"--list") ||
            beginWith(argv[optpart1],"--algo"))
            break;
    }
    if (optpart1 >= argc) {
        fputs("ERROR; 未知 Action.\n",stderr);
        return 1;
    }
    loadAlgorithm(optpart1,argv);

    int algoargc = argc - (optpart1 - 1);
    char **algoargv = &argv[optpart1 - 1];
    bool listopt = false;
    const char *algoname = 0;
    AlgoBehaviorConf conf;
    struct option options[] = {
        {"list",no_argument,0,'l'},
        {"algo",required_argument,0,'a'},
        {"help",no_argument,0,'h'},
        {"show",no_argument,0,'s'},
        {"no-show",no_argument,0,'S'},
        {"out",required_argument,0,'o'},
        {0,0,0,0}
    };

    int ret;
    optind = 0;
    while ((ret = parseCmdLine(algoargc,algoargv,":la:hsSo:",options,0)) != -1) {
        switch (ret) {
        case 'l':
            listopt = true;
            break;
        case 'a':
            algoname = optarg;
            break;
        case 'h':
            conf.help = true;
            break;
        case 's':
            conf.show = true;
            break;
        case 'S':
            conf.show = false;
            break;
        case 'o':
            conf.outfile = optarg;
            break;
        default:
            break;
        }
    }
    if (algoargc - optind > 1) 
        printf("WARNING;待处理图像个数: %d;然后当前算法并不支持批处理\n",
            algoargc - optind);
    else if (optind < algoargc) 
        conf.infile = algoargv[optind];

    if (listopt) {
        std::vector<std::string> algolist;
        NoiseAlgorithm::algoList(algolist);
        std::vector<std::string>::iterator iter = algolist.begin();
        for (int i = 0; iter != algolist.end(); ++iter,++i) 
            printf("INFO;已加载算法 %d: %s\n",i,iter->c_str());
    }

    if (algoname != 0) 
        executeAlgorithm(algoname,&conf,algoargc,algoargv);
    return 0;
}

